﻿#ifndef __INC_QG_DX9_DEV_H__
#define __INC_QG_DX9_DEV_H__

#ifdef _MSC_VER
#pragma once
#endif

#include "qg/qgasv.h"

// DX9
#if _SB_DEBUG_ && 0
#define D3D_DEBUG_INFO
#endif
#include <d3d9.h>
#include "3rd/DirectX9/d3dx9.h"

//////////////////////////////////////////////////////////////////////////
// 다이렉트X9 디바이스

#define DX9_SIGNAL_LOST			"DX9_LOST"
#define DX9_SIGNAL_RESET		"DX9_RESET"

// 디바이스
class DX9Device : public qgDevice
{
	KCH_SUBCLASS(DX9Device, qgDevice);
	KT_DECL_TYPE_INIT(DX9Device);
	KT_DECL_TYPE_FINAL(DX9Device);
	KCH_FRIEND(DX9Shader);
	KCH_FRIEND(DX9ShaderWorker);
	KCH_FRIEND(DX9TextureOff);

public:
	struct DeviceCreationData
	{
		UINT			adapter;
		D3DDEVTYPE		hal;
		HWND			hwnd;
	};

	struct Prsn : public qgDevPrsn
	{
		// 재질
		IDirect3DPixelShader9*	m_shd_ps;
		IDirect3DVertexShader9*	m_shd_vs;

		qgSplrData				m_splr_data[QGSGTEX_MAX_VALUE];
		qgRaszData				m_rasz_data;
		qgBlndData				m_blnd_data;
		bool					m_blnd_blend;
		bool					m_blnd_alpha;

		// 그리기
		kint					d_ind_vertexcount;

		// 세이더 프로파일
		char					prof_ps[16];
		char					prof_vs[16];

		kushort					cps_vs_max_bool;
		kushort					cps_vs_max_int;
		kushort					cps_vs_max_float;
		kushort					cps_ps_max_bool;
		kushort					cps_ps_max_int;
		kushort					cps_ps_max_float;

		// 포맷
		D3DFORMAT				fmt_back_buffer;
		D3DFORMAT				fmt_depth_stencil;
	};

	// 기본 오브젝트
	struct Debz : public qgDevDebz
	{
		qgBuffer*				vb;
		qgLayout*				lrtc;
	};

	// 설정
	struct InternalSet
	{
		D3DPRESENT_PARAMETERS	dpp;
		D3DDISPLAYMODEEX		ddm;

		bool					reset;
		bool					lost;

		bool					exmode;
		
		kint					enters;
	};

	// DLL 처리, D3D9
	typedef IDirect3D9* (WINAPI *pD3DCreate)(UINT);
	typedef HRESULT(WINAPI *pD3DCreateEx)(UINT, IDirect3D9Ex**);
	static pD3DCreate D3DCREATE;
	static pD3DCreateEx D3DCREATEEX;

	// DLL 처리, D3DX9
	typedef HRESULT(WINAPI *pDXCompileShader)(LPCSTR, UINT, CONST D3DXMACRO*, LPD3DXINCLUDE, LPCSTR, LPCSTR, DWORD, LPD3DXBUFFER*, LPD3DXBUFFER*, LPD3DXCONSTANTTABLE*);
	static pDXCompileShader DXCOMPILESHADER;

	//
	typedef HRESULT(WINAPI *pD3DXCreateCubeTextureFromFileExW)(LPDIRECT3DDEVICE9,LPCWSTR,UINT,UINT,DWORD,D3DFORMAT,D3DPOOL,DWORD,DWORD,D3DCOLOR,D3DXIMAGE_INFO*,PALETTEENTRY*,LPDIRECT3DCUBETEXTURE9*);
	static pD3DXCreateCubeTextureFromFileExW DXCREATECUBEFROMFILEEX;

private:
	IDirect3D9Ex*		_driver;
	IDirect3DDevice9Ex*	_device;

	IDirect3DSurface9*	_surface;
	IDirect3DSurface9*	_depthstencil;

	kSpinLock			_rlock;

	InternalSet			_vins;
	Prsn				_vprn;
	Debz				_vdeb;

private:
	DX9Device();
	~DX9Device();

public:
	IDirect3D9* GetDriver() { return _driver; }
	IDirect3DDevice9* GetDevice() { return _device; }

	bool IsEx() { return _vins.exmode; }
	const char* GetProfilePs() { return _vprn.prof_ps; }
	const char* GetProfileVs() { return _vprn.prof_vs; }
	D3DFORMAT GetBackBufferFormat() { return _vprn.fmt_back_buffer; }
	D3DFORMAT GetDepthStencilFormat() { return _vprn.fmt_depth_stencil; }

	bool TestEnableD3DFORMAT(D3DFORMAT fmt, DWORD mask, D3DRESOURCETYPE rtype);

public:
	K_OVR bool Layout(const qgSevLayout& e);

	K_OVR bool Enter(kint clearance);
	K_OVR bool Leave();
	K_OVR bool Flush();

	K_OVR void Draw(kint vertexcount, kint vertexstart);
	K_OVR void DrawIndexed(kint indexcount, kint vertexstart);

	K_OVR void DrawEnterIndirect(kint vertexcount, kint vertexstride, kpointer* outdata);
	K_OVR void DrawLeaveIndirect();

	K_OVR void DrawRect(const tpl::Rect&bound, const tpl::ColorValue& color, qgTexture* tex, const tpl::Quad* coord);
	K_OVR void DrawGlyph(const tpl::Rect&bound, const tpl::ColorValue& color, qgTexture* tex);
	K_OVR void DrawPolyLine(kint count, const tpl::Point* pts, const tpl::ColorValue* colors);
	K_OVR void DrawPolyStrip(kint count, const tpl::Point* pts, const tpl::ColorValue* colors, qgTexture* tex, const tpl::Vec2* coords);

	K_OVR qgImage* ScreenCapture();

	K_OVR bool _SetRenderTarget(qgTexture* tex);

protected:
	K_OVR bool _Initialize(qgStub* stub, kint flags);
	K_OVR bool _ResetPrepare(const tpl::Point& size, bool prepare);
	K_OVR void _Finalize();

	K_OVR qgBuffer* _RoamImplBuffer(const qgArprBuffer& arpr);
	K_OVR qgLayout* _RoamImplLayout(qgLayoutData* data);
	K_OVR qgTexture* _RoamImplTexture(const qgArprTexture& arpr);
	K_OVR qgTexture* _RoamImplTexture(const qgArprImage& arpr);
	K_OVR qgTexture* _RoamImplMotionTexture(const char* filename, kint flags);
	K_OVR qgDepthBuffer* _RoamImplDepthBuffer(const tpl::Point& size);
	K_OVR qgShader* _RoamImplShader(const char* name);
	K_OVR qgRasz* _RoamImplRasz(const qgRaszData& data);
	K_OVR qgBlnd* _RoamImplBlnd(const qgBlndData& data);
	K_OVR qgSplr* _RoamImplSplr(const qgSplrData& data);

private:
	bool _CreateDevice(const DeviceCreationData& data, D3DPRESENT_PARAMETERS& dpp, DWORD flags) throw();
	bool _CreateDeviceEx(const DeviceCreationData& data, D3DPRESENT_PARAMETERS& dpp, D3DDISPLAYMODEEX* ddm, DWORD flags) throw();
	void _CheckShaderVersion(D3DCAPS9& dcs) throw();
	void _ResetDevice() throw();

	void _InternalClear(kint clearance, const tpl::Color& color, kint stencil, float depth) throw();
	bool _InternalEnter(kint clearance, const tpl::Color& color, kint stencil, float depth) throw();
	bool _InternalLeave() throw();

	void _InternalDraw(kint texcount, kint vertexcount, kint vertexstart) throw();
	void _InternalDrawIndexed(kint texcount, kint indexcount, kint vertexstart) throw();

	bool _CommitTexture(kint stage, qgTexture* tex) throw();
	void _CommitSampler(kint stage, const qgSplrData& nsp, qgSplrData& psp) throw();
	void _CommitRasterize(const qgRaszData& nrz, qgRaszData& prz) throw();
	void _CommitBlendState(const qgBlndData& nbd, qgBlndData& pbd) throw();
	bool _CommitShader() throw();

	bool _BindMtrl(ksize_t count) throw();
	bool _BindDraw(bool bindindex) throw();
	bool _BindShader() throw();
};

#endif	// __INC_QG_DX9_DEV_H__
